package com.alg.local.gateway.filter;

import cn.hutool.json.JSONUtil;
import com.alg.local.gateway.entity.EncrProps;
import com.alg.local.gateway.util.GatewayUtil;
import com.alg.local.gateway.util.SymmetricEncryptionUtilCbc;
import com.alg.local.gateway.util.WebApiResult;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

/**
 * @author al
 * @date 2022/5/27 10:52
 * @description
 */
@Component
@Slf4j
public class EncryptResponseBodyFilter implements GlobalFilter, Ordered {

    @Autowired
    private EncrProps encrProps;

    private final PathMatcher pathMatcher = new AntPathMatcher();
    @Autowired
    RedissonClient redissonClient;

    @Autowired
    GatewayUtil gatewayUtil;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if (encrProps.isEnable()) {
            ServerHttpRequest request = exchange.getRequest();
            boolean canSkip = false;
            log.debug("免校验的URL（配置的白名单+swagger）");
            String requestUrl = request.getPath().toString();
            List<String> ignoreUrlList = encrProps.getIgnoreUrl();
            if (ignoreUrlList != null && ignoreUrlList.size() > 0) {
                canSkip = ignoreUrlList.stream().anyMatch(urlPattern -> pathMatcher.match(urlPattern, requestUrl));
            }

            List<String> ignoreUrlRspList = encrProps.getIgnoreUrlRsp();
            if (ignoreUrlRspList != null && ignoreUrlRspList.size() > 0) {
                canSkip = ignoreUrlRspList.stream().anyMatch(urlPattern -> pathMatcher.match(urlPattern, requestUrl));
            }

            if (canSkip) {
                return chain.filter(exchange);
            }

            log.info("[Gateway - ResponseBodyEncoder] 请求地址：{}，请求资源路径：{}", request.getRemoteAddress(), request.getPath());
            ServerHttpResponse originalResponse = exchange.getResponse();
            DataBufferFactory bufferFactory = originalResponse.bufferFactory();
            ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
                @Override
                public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                    if (body instanceof Flux) {
                        Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                        return super.writeWith(fluxBody.buffer().map(dataBuffer -> {

                            DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
                            DataBuffer join = dataBufferFactory.join(dataBuffer);

                            byte[] content = new byte[join.readableByteCount()];
                            join.read(content);
                            //释放掉内存
                            DataBufferUtils.release(join);
                            // 正常返回的数据
                            String rootData = new String(content, StandardCharsets.UTF_8);
                            WebApiResult apiResult = JSONUtil.toBean(rootData, WebApiResult.class);
                            log.info("加密前响应Body：{}", JSONUtil.toJsonStr(apiResult));
                            // 对数据进行加密
                            String encryptData = "";
                            if (apiResult.getData() != null && !"".equals(apiResult.getData())) {
                                String data = String.valueOf(apiResult.getData());
                                try {
                                    ServerHttpRequest request = exchange.getRequest();
                                    Map<String, String> aesKey = gatewayUtil.getClientAesKey(request);
                                    encryptData = SymmetricEncryptionUtilCbc.encrypt(aesKey.get("key"), aesKey.get("iv"), data);
                                    log.info("加密后响应Body：{}", encryptData);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                            apiResult.setData(encryptData);

                            // 加密后的数据返回给客户端
                            byte[] uppedContent = JSONUtil.toJsonStr(apiResult).getBytes();
                            return bufferFactory.wrap(uppedContent);
                        }));
                    }
                    return super.writeWith(body);
                }
            };
            return chain.filter(exchange.mutate().response(decoratedResponse).build());
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -1;
    }
}
